/**
 * @file ErrorHandler.h
 * @brief This class provides error handling functionalities, such as setting errors, retrieving errors, and managing an error deque.
 * 
 * The ErrorHandler class is responsible for managing errors by storing error messages, codes, and providing ways to retrieve them.
 * It supports registering a callback function that will be called whenever an error occurs.
 * It uses a deque to manage the errors, allowing for efficient addition and removal from both ends.
 * 
 * -------------------------------------------------------------------------------------------------------------------------
 * REVISION HISTORY
 * -------------------------------------------------------------------------------------------------------------------------
 * | Version   | Date       | Author      | Ticket Id | Description                                           |
 * |-----------|------------|-------------|-----------|-------------------------------------------------------|
 * | 1.0       | 2024-09-24 | zhiyu5.liu  | N/A    | Initial definition of the ErrorHandler class.       |
 * 
 * 
 * 
 * -------------------------------------------------------------------------------------------------------------------------
 * COPYRIGHT
 * -------------------------------------------------------------------------------------------------------------------------                                                                
 * 
 * \verbatim
 * 
 * Copyright (c) 2024 by <company name>. All rights reserved.
 * \endverbatim 
 */
#ifndef ERROR_HANDLER_H
#define ERROR_HANDLER_H

#include <atomic>
#include <chrono>
#include <deque>
#include <functional>
#include <mutex>
#include <optional>
#include <queue>
#include <string>
#include "GlobalErrorDefs.h"

namespace errors{
/**
 * @enum ErrorSeverity
 * @brief Enum defining the severity levels of errors.
 * 
 * This enum defines the severity levels of errors that can be used to categorize the severity of an error.
 * @see lclog::LogLevel,ErrorSeverity is same as lclog::LogLevel
 * 
 */
enum ErrorSeverity
{
    Debug = 1,  ///< Debug level for detailed debugging information.
    Info,   ///< Info level for general operational messages.
    Warn,   ///< Warn level for potential issues that do not halt the system.
    Error,  ///< Error level for significant problems that require attention.
    Fatal   ///< Fatal level for critical issues that lead to system termination.
};

/**
 * @class ErrorHandler
 * @brief This class provides error handling functionalities, such as setting errors, retrieving errors, and managing an error deque.
 *
 * The ErrorHandler class is responsible for managing errors by storing error messages, codes, and providing ways to retrieve them.
 * It supports registering a callback function that will be called whenever an error occurs.
 * It uses a deque to manage the errors, allowing for efficient addition and removal from both ends.
 */
class ErrorHandler {
public:
    /**
     * @struct ErrorInfo
     * @brief A structure representing an error's details.
     * 
     * This structure contains the error message, error code, and a flag indicating whether there is an error.
     */
    struct ErrorInfo {
        std::string message; ///< The error message.
        ErrorCode code;     ///< The error code associated with the error.
        size_t  occurrences; ///< The number of occurrences of the error.
        std::chrono::steady_clock::time_point timestamp; ///< Timestamp when the error occurred.
        ErrorSeverity severity; ///< The severity of the error.
        std::string source; ///< The source of the error.
        
        /**
         * @brief Default constructor that initializes the error code to `ErrorCode::Success` and `hasError` to false.
         */
        ErrorInfo() noexcept : code(ErrorCode::Success), timestamp(std::chrono::steady_clock::now()), severity(ErrorSeverity::Error), source("") {}

        /**
         * @brief Constructor that initializes the error message and error code.
         * @param msg The error message.
         * @param errCode The error code.
         */
        ErrorInfo(const std::string& msg, ErrorCode errCode) noexcept : message(msg), code(errCode), timestamp(std::chrono::steady_clock::now()), severity(ErrorSeverity::Error), source("") {}

        /**
         * @brief Constructor that initializes the error message, error code, severity, and source.
         * @param msg The error message.
         * @param errCode The error code.
         * @param severity The severity of the error.
         * @param source The source of the error.
         */
        ErrorInfo(const std::string& msg, ErrorCode errCode, ErrorSeverity severity, const std::string& source) noexcept : message(msg), code(errCode), timestamp(std::chrono::steady_clock::now()), severity(severity), source(source) {}
    };

    /**
     * @brief Default constructor.
     * Initializes the ErrorHandler instance.
     */
    ErrorHandler(): m_coalescingTimeWindow(std::chrono::milliseconds(1000)), m_maxErrorCount(1000) {}

    /**
     * @brief Constructor that initializes the coalescing time window and max error count.
     * @param coalescingTimeWindow The time window for coalescing errors.
     * @param maxErrorCount The maximum number of errors to store.
     */
    ErrorHandler(std::chrono::milliseconds coalescingTimeWindow, uint16_t maxErrorCount) : m_coalescingTimeWindow(coalescingTimeWindow), m_maxErrorCount(maxErrorCount) {}

    /**
     * @brief Default destructor.
     * Ensures any resources used by the ErrorHandler are cleaned up.
     */
    ~ErrorHandler() noexcept = default;

    /**
     * @brief Registers an error callback function.
     * 
     * @param callback The callback function to be called when an error occurs.
     * @param enqueueErrors Flag indicating whether to enqueue errors (default is true).
     */
    void setErrorCallback(std::function<void(const ErrorInfo&)> callback, bool enqueueErrors = true);

    /**
     * @brief Sets an error by providing an error message and an error code.
     * The error will be added to the deque if `enqueueErrors` is true.
     * 
     * @param[in] msg The error message.
     * @param[in] code The error code associated with the error.
     * @param[in] severity The severity of the error.
     * @param[in] source The source of the error.
     */
    void setError(const std::string& msg, ErrorCode code, ErrorSeverity severity = ErrorSeverity::Error, const std::string& source = "");

    /**
     * @brief Retrieves the next error from the error deque.
     * 
     * @return An optional ErrorInfo object containing the error details if there is an error, otherwise `std::nullopt`.
     */
    [[nodiscard("need to check if there is an error")]] std::optional<ErrorInfo> getNextError();

    /**
     * @brief Checks if there are any errors in the error deque.
     * 
     * @return `true` if there is at least one error, `false` otherwise.
     */
    [[nodiscard("need to check if there is an error")]] bool hasError() const noexcept;

    /**
     * @brief Retrieves the error message of the first error in the deque without removing it.
     * 
     * @param[in] removeError Flag indicating whether to remove the error from the deque.
     * @return The error message if there is an error, or an empty string if the deque is empty.
     */
    [[nodiscard("need to check if there is an error")]] std::string getErrorMessage(bool removeError = false)  noexcept;

    /**
     * @brief Retrieves the error code of the first error in the deque without removing it.
     * 
     * @param[in] removeError Flag indicating whether to remove the error from the deque.
     * @return The error code of the first error, or `ErrorCode::Success` if the deque is empty.
     */
    [[nodiscard("need to check if there is an error")]] ErrorCode getErrorCode(bool removeError = false) noexcept;

    /**
     * @brief Retrieves the total number of errors in the error deque.
     * 
     * @param[in] level The severity level of the error.
     * @return The number of errors currently stored in the deque.
     */
    [[nodiscard("need to check if there is an error")]] size_t getErrorCount(int level = -1) noexcept;

    /**
     * @brief Clears all the errors in the error deque.
     */
    void clearErrors() noexcept;

    /**
     * @brief Sets the coalescing time window for errors.
     * 
     * @param[in] timeWindow The time window in milliseconds.
     */
    void setCoalescingTimeWindow(std::chrono::milliseconds timeWindow);

    /**
     * @brief Sets the maximum number of errors to store.
     * 
     * @param[in] maxCount The maximum number of errors to store.
     */
    void setMaxErrorCount(uint16_t maxCount);

    /**
     * @brief Gets the coalescing time window for errors.
     * 
     * @return The time window in milliseconds.
     */
    [[nodiscard("need to check if there is an error")]] std::chrono::milliseconds getCoalescingTimeWindow() const noexcept;

    /**
     * @brief Gets the maximum number of errors to store.
     * 
     * @return The maximum number of errors to store.
     */
    [[nodiscard("need to check if there is an error")]] uint16_t getMaxErrorCount() const noexcept;
private:
    mutable std::mutex m_errorMutex;                       ///< Mutex for protecting access to the error deque.
    std::deque<ErrorInfo> m_errorQueue;                    ///< Queue storing error information.
    std::mutex m_callbackMutex;                            ///< Mutex for protecting the callback.
    std::function<void(const ErrorInfo&)> m_errorCallback; ///< The callback function for errors.
    std::atomic<bool> m_enqueueErrors{true};               ///< Flag to control whether errors should be enqueued.
    std::chrono::milliseconds m_coalescingTimeWindow;  ///< The time window for coalescing errors.
    uint16_t m_maxErrorCount;  ///< The maximum number of errors to store.
    /**
     * @brief Safely invokes the error callback if it is set.
     * 
     * @param error The error information to be passed to the callback.
     */
    void callErrorCallback(const ErrorInfo& error);
};

} // namespace ipc

#endif // ERROR_HANDLER_H
